/*
OpenMVL Matrix Vector Library
Copyright (c) 2009 FluidInteractive (R)
 
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising
from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
 
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
 
Written by: Nicola Candussi <nicola@fluidinteractive.com>
*/

//layout_project.hpp

#ifndef MVL_LAYOUT_PROJECT_HPP
#define MVL_LAYOUT_PROJECT_HPP

#include "layout.hpp"

namespace mvl {

namespace layout {

//project entries
template<typename I, typename J, typename Tag, typename Src>
struct project_entry {
    typedef Tag                 tag;           //type of entry
    typedef sparse_layout_tag   layout_tag;    //type of layout 
    typedef I                   i;
    typedef J                   j;
    typedef Src                 src;    //refers to the entry in the source layout
    typedef project_entry       type;
};

//create a new layout with entries than satisfy ConditionFn1 and ConditionFn2
template<typename L, 
         typename ConditionFn1,
         typename ConditionFn2,
         typename NewFn,
         typename NewSize2>
struct layout_project
{
    typedef L layout_t;
    typedef typename layout_t::order order;
    typedef typename begin1<layout_t>::type begin;
    typedef typename end1<layout_t>::type end;

    template<typename Data, typename Pos1>
    struct state1 {
        typedef Data        data;
        typedef Pos1        pos1;
        typedef state1      type;
    };

    struct back_inserter1 {
        template<typename State, typename It>
        struct apply {
            typedef typename meta::deref<It>::type e;
            typedef typename e::i i;
            typedef typename e::j j;
            typedef typename State::pos1 pos1;
            typedef typename get2<i, j, order>::type pos2;
            typedef typename ConditionFn2::template apply<pos1, pos2>::type condition;
            typedef typename NewFn::template apply<e>::type new_entry;
            typedef typename meta::eval_if<
                condition,
                meta::push_back<
                    typename State::data,
                    new_entry
                >,
                meta::identity<typename State::data>
            >::type new_data;
            typedef state1<
                new_data,
                pos1
            > type;
        };
    };

    template<typename Data, typename NumVals>
    struct state2 {
        typedef Data data;
        typedef NumVals num_vals;
        typedef state2 type;
    };

    struct back_inserter2 {
        template<typename State, typename It>
        struct apply {
            typedef typename meta::distance<
                begin,
                It
            >::type pos1;
            typedef typename ConditionFn1::template apply<pos1>::type condition;
            typedef typename meta::eval_if<
                condition,
                typename meta::iter_fold<
                    typename begin2<It>::type,
                    typename end2<It>::type,
                    state1<
                        meta::vector<>,
                        pos1
                    >,
                    back_inserter1
                >,
                state1<
                    meta::vector<>,
                    pos1
                >
            >::type new_element;

            typedef typename meta::eval_if<
                condition,
                meta::push_back<
                    typename State::data,
                    typename new_element::data
                >,
                meta::identity<typename State::data>
            >::type new_data;

            typedef typename meta::plus<
                typename State::num_vals,
                typename meta::size<typename new_element::data>::type
            >::type new_num_vals;

            typedef state2<
                new_data,
                new_num_vals
            > type;
        };
    };

    typedef typename meta::iter_fold<
        begin,
        end,
        state2<
            meta::vector<>,
            meta::int_<0>
        >,
        back_inserter2
    >::type result;
    typedef typename meta::size<typename result::data>::type new_size1;
    typedef NewSize2 new_size2;
    typedef typename sparse_layout<
        get1<new_size1, new_size2, order>::value,
        get2<new_size1, new_size2, order>::value,
        typename L::order,
        typename result::data,
        result::num_vals::value
    >::type type;
};

} //namespace layout

} //namespace mvl


#endif

